package com.lightsh.motioner;

import android.graphics.PointF;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * 该类负责跟踪{@link View#onTouchEvent(MotionEvent)}事件所产生的活动点,产生动作各种事件
 *
 */
public class MultiPointTracker {
	
	private static final String TAG = "MultiPointTracker";
	private static final boolean DEBUG = false;
	
	protected float mLastMotionX;
	protected float mLastMotionY;
	
	private OnGestureTrackListener mListener;
	
	/**
     * 当前没有活动点的标志
     */
    private static final int INVALID_POINTER = -1;
	/**
     * 当前活动的MotionEvent点的id值,处理多点触摸
     */
    private int mActivePointerId = INVALID_POINTER;
    
    /**
     * 第二个活动的点，该点只作用于zoom模式下进行缩放
     */
    private int mActivePointerId2 = INVALID_POINTER;
    
    private enum MOTION_MODE{
    	NONE,
    	DRAP,
    	ZOOM
    }
    private MOTION_MODE mMode = MOTION_MODE.NONE;
    
    /**
     * 记录上一次两个活动点的距离
     */
    private float mLastDistance;
	
    
    public MultiPointTracker(OnGestureTrackListener listenr){
    	if(listenr == null){
    		throw new IllegalArgumentException("listener can not be null!");
    	}
    	mListener = listenr;
    }
    
    /**
     * 调用该方法来相应用户动作
     */
    public boolean onTouchEvent(MotionEvent ev){
		final int action = ev.getAction();
		
		switch(action & MotionEvent.ACTION_MASK){
		case MotionEvent.ACTION_DOWN:{
			mLastMotionX = ev.getX();
			mLastMotionY = ev.getY();			
			
			// 记录活动点，这里消息的活动点一定是0，并且ev中应该只有一个点
            mActivePointerId = ev.getPointerId(0);
            mActivePointerId2 = INVALID_POINTER;
			mMode = MOTION_MODE.DRAP;
			logD("mMode -> DRAP");
			break;
		}
		case MotionEvent.ACTION_MOVE:{
			switch(mMode){
			case DRAP:{
				int activePointerIndex = ev.findPointerIndex(mActivePointerId);
	        	if(activePointerIndex < 0 || activePointerIndex > ev.getPointerCount() - 1){
	        		// 我们需要换一个点
	        		changeActivePoint(0, ev);
	        		activePointerIndex = 0;
	        	}
				
				final float x = ev.getX(activePointerIndex);
				final float y = ev.getY(activePointerIndex);
				
				boolean handle = mListener.onDrag(mLastMotionX, mLastMotionY, x, y);
				
				mLastMotionX = x;
				mLastMotionY = y;
				return handle;
			}
			case ZOOM:{
				float newDistance = getDistance(ev);
				PointF point = getMidPoinF(ev);
				if(DEBUG){
					float distance = mLastDistance - newDistance;
					if(Math.abs(distance) > 5f){
						logD("point = " + point.x + "," + point.y);
						logD("distance = " + distance);
					}
				}
				boolean handle = mListener.onZoom(mLastDistance, newDistance, point);
				mLastDistance = newDistance;
				return handle;
			}
			}
			
			break;
		}
		case MotionEvent.ACTION_UP:{
			mActivePointerId = INVALID_POINTER;
			mActivePointerId2 = INVALID_POINTER;
			mMode = MOTION_MODE.NONE;
			logD("mMode -> NONE");
			break;
		}
		case MotionEvent.ACTION_CANCEL:{
			mActivePointerId = INVALID_POINTER;
			mActivePointerId2 = INVALID_POINTER;
			mMode = MOTION_MODE.NONE;
			logD("mMode -> NONE");
			break;
		}
		case MotionEvent.ACTION_POINTER_UP:{
			onSecondaryPointerUp(ev);
			break;
		}
		case MotionEvent.ACTION_POINTER_DOWN:{
			onSecondaryPointerDown(ev);
			break;
		}
		}
		
		return mMode != MOTION_MODE.NONE ;
	}
    
    public void setOnPointTrackerListener(OnGestureTrackListener listener){
    	mListener = listener;
    }
	
	private void onSecondaryPointerUp(MotionEvent ev) {
		logD("onSecondaryPointerUp");
		final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;	
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == mActivePointerId) {
        	logD("mActivePionterId up");
        	// 活动点up了，我们获取下一个点作为活动点
        	int newPointerIndex;
            if(mActivePointerId2 == INVALID_POINTER){
            	newPointerIndex = pointerIndex == 0 ? 1 : 0;
            }else{
            	newPointerIndex = ev.findPointerIndex(mActivePointerId2);
            }
            changeActivePoint(newPointerIndex, ev);
            findActivePoint2(ev, pointerIndex);
        }else if(pointerId == mActivePointerId2){
        	logD("mActivePointerId2 up");
        	findActivePoint2(ev, pointerIndex);
        }
        // 如果这里将状态变为DRAP会导致误操作
        if(mActivePointerId2 == INVALID_POINTER){
        	mMode = MOTION_MODE.NONE;
        	logD("mMode -> NONE");
        }
        
    }
	
	/**
	 * 当有多于一个点按下该方法调用，需要确定当前活动的点
	 * <br/>暂时支持2个，当多于2个点按下时，只算前两个点
	 */
	private void onSecondaryPointerDown(MotionEvent ev){
		logD("onSecondaryPointerDown");
		if(mActivePointerId2 == INVALID_POINTER){
			final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
			final int pointerId = ev.getPointerId(pointerIndex);
			mActivePointerId2 = pointerId;
			logD("mActivePointerId2 -> " + pointerId);
		}
		if(mMode != MOTION_MODE.ZOOM){
			logD("mMode -> ZOOM");
			mMode = MOTION_MODE.ZOOM;
		}
		mLastDistance = getDistance(ev);
	}
	
    private void changeActivePoint(int newPointerIndex, MotionEvent ev){
    	mLastMotionX = ev.getX(newPointerIndex);
    	mLastMotionY = ev.getY(newPointerIndex);
        mActivePointerId = ev.getPointerId(newPointerIndex);
        logD("mActivePointerId -> " + mActivePointerId);
//        if (mVelocityTracker != null) {
//            mVelocityTracker.clear();
//        }
    }
    
    /**
     * 寻找第二点，注意不能和第一点重复，并且可能不存在第二点
     */
    private void findActivePoint2(MotionEvent ev, int upIndex){
    	final int count = ev.getPointerCount();
    	for(int i = 0; i < count; ++i){
    		if(i == upIndex){
    			continue;
    		}
    		int point2 = ev.getPointerId(i);
			if(point2 != mActivePointerId){
				mActivePointerId2 = point2;
				logD("mActivePointerId2 -> " + mActivePointerId2);
				mLastDistance = getDistance(ev);
				return;
			}
    	}
    	logD("mActivePointerId2 -> INVALID_POINTER");
    	mActivePointerId2 = INVALID_POINTER;
    }
    
    private void logD(String tip){
    	if(DEBUG){
    		Log.d(TAG, "" + tip);
    	}
    }
    
    
	/**
	 *  求两点的距离
	 */
	private float getDistance(MotionEvent ev) {
		final int index1 = ev.findPointerIndex(mActivePointerId);
		final int index2 = ev.findPointerIndex(mActivePointerId2);
		float eX = ev.getX(index1) - ev.getX(index2);
		float eY = ev.getY(index1) - ev.getY(index2);
		return FloatMath.sqrt(eX * eX + eY * eY);
	}

	/**
	 *  求2点的中心点
	 */
	private PointF getMidPoinF(MotionEvent ev) {
		final int index1 = ev.findPointerIndex(mActivePointerId);
		final int index2 = ev.findPointerIndex(mActivePointerId2);
		float x = (ev.getX(index1) + ev.getX(index2)) / 2;
		float y = (ev.getY(index1) + ev.getY(index2)) / 2;
		return new PointF(x, y);
	}
	
	public interface OnGestureTrackListener{
		/**
		 * 当用户拖动屏幕时触发
		 * @param lastX		拖动前的点的x坐标
		 * @param lastY		拖动前的点的y坐标
		 * @param currentX	拖动后的x坐标
		 * @param currenY	拖动后的y坐标
		 * @return			是否处理
		 */
		boolean onDrag(float lastX, float lastY, float currentX, float currentY);
		/**
		 * 当用户两个手指在屏幕上移动时触发
		 * @param lastDistance			zoom前两个手指的距离
		 * @param currentDistance		zoom后两个手指的距离
		 * @param centerPoint			zoom后两个点的中心点
		 * @return						是否处理
		 */
		boolean onZoom(float lastDistance, float currentDistance, PointF centerPoint);
	}
	
}
